﻿namespace Groundwork.Extensions
{
    using System;
    using System.Diagnostics.CodeAnalysis;
    using System.Security.Cryptography;
    using System.Text;

    /// <summary>
    /// Security Extensions Provide a way o protecting your data.
    /// </summary>
    [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Sha hash algorithm is a valid name")] 
    public static class Security
    {
        /// <summary>
        /// Provides a protected byte array given an input string
        /// </summary>
        /// <param name="value">current value to protect</param>
        /// <param name="entropy">optional string for entropy</param>
        /// <param name="scope">protection scope</param>
        /// <returns>protected byte array</returns>
        /// <exception cref="System.ArgumentNullException">Throws when the input value is null or empty</exception>
        /// <example>
        ///   <code><![CDATA[
        /// // basic protection
        /// internal void Example1()
        /// {
        /// 	string myString = "Hello World";
        /// 	byte[] protected = myString.Protect();
        /// }
        /// 
        /// // with additional entropy
        /// internal void Example2()
        /// {
        /// 	string myString = "Hello World";
        /// 	string entropy = Guid.NewGuid.ToString();
        /// 	byte[] protected = myString.Protect(entropy);
        /// }
        /// 
        /// // with additional entropy & Data Protection Scope
        /// internal void Example2()
        /// {
        /// 	string myString = "Hello World";
        /// 	string entropy = Guid.NewGuid.ToString();
        /// 	byte[] protected = myString.Protect(entropy, DataProtectionScope.LocalMachine);
        /// }
        /// ]]></code></example>
        public static byte[] Protect(this string value, string entropy = "", DataProtectionScope scope = DataProtectionScope.CurrentUser)
        {
            value.ThrowIfNullOrEmpty("Cannot protect nulls");
            return ProtectedData.Protect(value.ToByteArray(), entropy.ToByteArray(), scope);
        }

        /// <summary>
        /// Un protects the specified byte array and outputs an string representation of that array
        /// </summary>
        /// <param name="value">current byte array to un protect</param>
        /// <param name="entropy">optional entropy</param>
        /// <param name="scope">protection scope</param>
        /// <returns>unprotected string</returns>
        /// <example>
        ///   <code><![CDATA[
        /// // basic protection
        /// internal void Example1()
        /// {
        /// 	string myString = "Hello World";
        /// 	byte[] protected = myString.Protect();
        /// 	string unprotected = protected.UnProtect();
        /// }
        /// 
        /// // with additional entropy
        /// internal void Example2()
        /// {
        /// 	string myString = "Hello World";
        /// 	string entropy = Guid.NewGuid.ToString();
        /// 	byte[] protected = myString.Protect(entropy);
        /// 	string unprotected = protected.UnProtect(entropy);
        /// }
        /// 
        /// // with additional entropy & Data Protection Scope
        /// internal void Example2()
        /// {
        /// 	string myString = "Hello World";
        /// 	string entropy = Guid.NewGuid.ToString();
        /// 	byte[] protected = myString.Protect(entropy, DataProtectionScope.LocalMachine);
        /// 	string unprotected = protected.UnProtect(entropy, DataProtectionScope.LocalMachine);
        /// }
        /// ]]></code></example>
        public static string UnProtect(this byte[] value, string entropy = "", DataProtectionScope scope = DataProtectionScope.CurrentUser)
        {
            try
            {
                byte[] protectedBytes = ProtectedData.Unprotect(value, entropy.ToByteArray(), scope);
                return protectedBytes.AsString();
            }
            catch (CryptographicException exception)
            {
                throw exception;
            }
        }

        /// <summary>
        /// Generates a SHA1 hash from the input string
        /// </summary>
        /// <param name="value">current value</param>
        /// <returns>SHA1 hash of input value</returns>
        public static string ToSha1(this string value)
        {
            SHA1 sha1 = new SHA1CryptoServiceProvider();
            byte[] hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(value));
            string hexString = BitConverter.ToString(hash).Replace("-", string.Empty);
            return hexString;
        }

        /// <summary>
        /// Generates an MD5 hash from the input string
        /// </summary>
        /// <param name="value">current value</param>
        /// <returns>MD5 hash of input value</returns>
        public static string ToMd5(this string value)
        {
            MD5 md5 = new MD5CryptoServiceProvider();
            byte[] hash = md5.ComputeHash(Encoding.UTF8.GetBytes(value));
            string hexString = BitConverter.ToString(hash).Replace("-", string.Empty);
            return hexString;
        }

        /// <summary>
        /// Generates an Sha256 hash from the input string
        /// </summary>
        /// <param name="value">current value</param>
        /// <returns>Sha256 hash of input value</returns>
        public static string ToSha256(this string value)
        {
            SHA256 sha256 = new SHA256CryptoServiceProvider();
            byte[] hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(value));
            string hexString = BitConverter.ToString(hash).Replace("-", string.Empty);
            return hexString;
        }
    }
}
